package com.xzq.erp.utils;

import com.xzq.erp.properties.MinioProperties;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
public class MinioUtil {

    @Autowired
    private MinioProperties minioProperties;

    @Autowired
    private MinioClient minioClient;

    /**
     * 上传文件
     * @param file 文件对象
     * @return 文件访问URL
     * @throws Exception 上传异常
     */
    public String uploadFile(MultipartFile file) throws Exception {
        // 判断文件是否为空
        if (file == null || file.getSize() == 0) {
            log.error("==> 上传文件异常：文件大小为空 ...");
            throw new RuntimeException("文件大小不能为空");
        }

        // 文件的原始名称
        String originalFileName = file.getOriginalFilename();
        // 文件的 Content-Type
        String contentType = file.getContentType();

        // 生成存储对象的名称（将 UUID 字符串中的 - 替换成空字符串）
        String key = UUID.randomUUID().toString().replace("-", "");
        // 获取文件的后缀，如 .jpg
        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));

        // 拼接上文件后缀，即为要存储的文件名
        String objectName = String.format("%s%s", key, suffix);

        log.info("==> 开始上传文件至 Minio, ObjectName: {}", objectName);
        
        // 上传文件至 Minio
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(contentType)
                .build());

        // 返回文件的访问链接
        String url = String.format("%s/%s/%s", minioProperties.getEndpoint(), minioProperties.getBucketName(), objectName);
        log.info("==> 上传文件至 Minio 成功，访问路径: {}", url);
        return url;
    }

    /**
     * 上传文件（指定存储路径）
     * @param file 文件对象
     * @param objectName 存储路径（包括文件名）
     * @return 文件访问URL
     * @throws Exception 上传异常
     */
    public String uploadFile(MultipartFile file, String objectName) throws Exception {
        if (file == null || file.getSize() == 0) {
            throw new RuntimeException("文件大小不能为空");
        }
        if (StringUtils.isBlank(objectName)) {
            throw new RuntimeException("存储路径不能为空");
        }

        log.info("==> 开始上传文件至 Minio, ObjectName: {}", objectName);
        
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                .build());

        String url = String.format("%s/%s/%s", minioProperties.getEndpoint(), minioProperties.getBucketName(), objectName);
        log.info("==> 上传文件至 Minio 成功，访问路径: {}", url);
        return url;
    }

    /**
     * 删除文件
     * @param objectName 文件路径
     * @throws Exception 删除异常
     */
    public void deleteFile(String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .build());
        log.info("==> 删除文件成功, ObjectName: {}", objectName);
    }

    /**
     * 批量删除文件
     * @param objectNames 文件路径列表
     * @throws Exception 删除异常
     */
    public void deleteFiles(List<String> objectNames) throws Exception {
        List<DeleteObject> objects = objectNames.stream()
                .map(DeleteObject::new)
                .collect(Collectors.toList());
        
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder()
                .bucket(minioProperties.getBucketName())
                .objects(objects)
                .build());

        for (Result<DeleteError> result : results) {
            DeleteError error = result.get();
            log.error("==> 删除文件失败: {}, 错误信息: {}", error.objectName(), error.message());
        }
    }

    /**
     * 下载文件
     * @param objectName 文件路径
     * @return 文件流
     * @throws Exception 下载异常
     */
    public InputStream downloadFile(String objectName) throws Exception {
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .build());
    }

    /**
     * 获取文件临时访问URL
     * @param objectName 文件路径
     * @param expiry 过期时间（以秒为单位）
     * @return 临时访问URL
     * @throws Exception 生成URL异常
     */
    public String getPresignedObjectUrl(String objectName, int expiry) throws Exception {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .expiry(expiry, TimeUnit.SECONDS)
                .build());
    }

    /**
     * 获取文件元数据
     * @param objectName 文件路径
     * @return 文件元数据
     * @throws Exception 获取元数据异常
     */
    public StatObjectResponse getFileMetadata(String objectName) throws Exception {
        return minioClient.statObject(StatObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .build());
    }

    /**
     * 检查文件是否存在
     * @param objectName 文件路径
     * @return true:存在，false:不存在
     */
    public boolean isFileExist(String objectName) {
        try {
            minioClient.statObject(StatObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(objectName)
                    .build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 创建存储桶
     * @param bucketName 存储桶名称
     * @throws Exception 创建异常
     */
    public void createBucket(String bucketName) throws Exception {
        boolean exists = minioClient.bucketExists(BucketExistsArgs.builder()
                .bucket(bucketName)
                .build());
        if (!exists) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        }
    }

    /**
     * 列出所有存储桶
     * @return 存储桶列表
     * @throws Exception 获取列表异常
     */
    public List<Bucket> listBuckets() throws Exception {
        return minioClient.listBuckets();
    }

    /**
     * 列出存储桶中的所有文件
     * @param prefix 前缀
     * @param recursive 是否递归查找
     * @return 文件信息列表
     * @throws Exception 获取列表异常
     */
    public Iterable<Result<Item>> listFiles(String prefix, boolean recursive) throws Exception {
        return minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(minioProperties.getBucketName())
                .prefix(prefix)
                .recursive(recursive)
                .build());
    }

    /**
     * 将字节数组上传为文件
     * @param bytes 字节数组
     * @param objectName 文件路径
     * @param contentType 内容类型
     * @return 文件访问URL
     * @throws Exception 上传异常
     */
    public String uploadBytes(byte[] bytes, String objectName, String contentType) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .stream(bais, bytes.length, -1)
                .contentType(contentType)
                .build());

        String url = String.format("%s/%s/%s", minioProperties.getEndpoint(), minioProperties.getBucketName(), objectName);
        log.info("==> 上传字节数组至 Minio 成功，访问路径: {}", url);
        return url;
    }

    /**
     * 复制文件
     * @param sourceObjectName 源文件路径
     * @param targetObjectName 目标文件路径
     * @throws Exception 复制异常
     */
    public void copyFile(String sourceObjectName, String targetObjectName) throws Exception {
        minioClient.copyObject(CopyObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(targetObjectName)
                .source(CopySource.builder()
                        .bucket(minioProperties.getBucketName())
                        .object(sourceObjectName)
                        .build())
                .build());
        log.info("==> 复制文件成功, 源文件: {}, 目标文件: {}", sourceObjectName, targetObjectName);
    }
}